/*
 * hd44870.c
 *
 *  Created on: Nov 21, 2010
 *      Author: Orlando Arias
 *     License: GPL v3
 *
 *    hd44870-spi
 *    Copyright (C) 2010  Orlando Arias
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */



#include "hd44780.h"
#include <util/delay.h>

uint8_t _spi_send(uint8_t c);
void lcd_write(uint8_t data, uint8_t rs);

void lcd_init(){
	// set up SPI interface and strobe
	DDRB = _BV(PB5) | _BV(PB3) | _BV(PB2);
	_lcd_glue(DDR, _str_port) |= _BV(_str_bit);
	SPCR = _BV(SPE) | _BV(MSTR);
	SPSR = _BV(SPI2X);
	__asm("nop");

	// set up LCD interface
	// carry over LSB by 1 for RS.
	_delay_ms(40);
	_spi_send(0x03 << 1);
	_lcd_strobe();
	_delay_ms(4.1);
	_spi_send(0x03 << 1);
	_lcd_strobe();
	_delay_ms(0.2);
	_spi_send(0x03 << 1);
	_lcd_strobe();
	_delay_ms(0.2);
	_spi_send(0x02 << 1);
	_lcd_strobe();
	_delay_ms(5);

	lcd_sendCommand(0x28);
	lcd_sendCommand(BLANK_DISPLAY);
	lcd_sendCommand(CURSOR_INVISIBLE);
	lcd_sendCommand(EM_INCREMENT_OFF);
	lcd_sendCommand(CLEAR);
}

void lcd_sendCommand(command cmd){
	lcd_write(cmd, 0);
	if (cmd == CLEAR)
		_delay_ms(5);		// adjust for clear
}

int lcd_writeChar(char c, FILE *unused){
	lcd_write(c, 1);
	_delay_ms(0.5);
	return 0;
}

// TODO: Optimize
void lcd_write(uint8_t data, uint8_t rs){
	uint8_t nibble = (data >> 3);
	nibble &= ~_BV(0);
	if (rs)
		nibble |= _BV(0);
	_spi_send(nibble);
	_lcd_strobe();
	nibble = (data << 1);
	if (rs)
		nibble |= _BV(0);
	_spi_send(nibble);
	_lcd_strobe();
	_delay_us(100);
}

uint8_t _spi_send(uint8_t c){
	SPDR = c;
	loop_until_bit_is_set(SPSR, SPIF);
	_delay_us(2);
	return SPDR;
}
